#include <sstream>
#include <atlstr.h>
#include <atltypes.h>
#include <atlbase.h>
#include <atlapp.h>
#include <atlwin.h>
#include <atlctrls.h>
#include <atldlgs.h>
#include <CommonControls.h>
#include "Setting.h"
#include "Language.h"
#include "Uploader.h"
#include "resource.h"
#include "MainWindow.h"
#include "DlgAbout.h"

extern CAppModule _Module;

//////////////////////////////////////////////////////////////////////////
// defines
#define UI_EFFECT_BACKGROUND_TRANSPARENT 212


//////////////////////////////////////////////////////////////////////////
// static
LPCTSTR CMainWindow::m_lpszWindowName = _T("XL / uploader");
int CMainWindow::m_tabHeight = 20;


//////////////////////////////////////////////////////////////////////////
// protected methods

CMainWindow::CMainWindow(void)
	: m_fileList(this)
	, m_imageCtrl(&m_document)
	, m_titleUpdated(true)
{
	for (int i = 0; i < COUNT_OF(m_statics); ++ i) {
		m_statics[i].setSibling(&m_tab);
	}
	m_hyperMakeDonation.setSibling(&m_tab);
	m_hyperSite.setSibling(&m_tab);
	m_hyperSite.setAlign(CHyperLink::RIGHT);
}

CMainWindow::~CMainWindow(void) {
}

tstring CMainWindow::_GetCtrlText(_IDS ctrlId) {
	CLanguage *pLanguage = CLanguage::getInstance();
	switch (ctrlId) {
	case ID_ADD_IMAGE:
		return pLanguage->getString(_T("Add Image..."));
	case ID_START_UPLOAD:
		return pLanguage->getString(_T("Upload"));
	case ID_STOP_UPLOAD:
		return pLanguage->getString(_T("Stop"));
	case ID_CLEAR_ALL:
		return pLanguage->getString(_T("Clear"));
	case ID_ABOUT:
		return pLanguage->getString(_T("About..."));

	case ID_STATIC_URL:
		return pLanguage->getString(_T("URL"));
	case ID_STATIC_UBB:
		return pLanguage->getString(_T("UBB"));
	case ID_STATIC_HTML:
		return pLanguage->getString(_T("HTML"));

	case ID_BUTTON_COPY_URL:
	case ID_BUTTON_COPY_UBB:
	case ID_BUTTON_COPY_HTML:
		return pLanguage->getString(_T("Copy"));

	case ID_MAKE_DONATION:
		return pLanguage->getString(_T("Make a donation"));
	case ID_SITE_LINK:
		return pLanguage->getString(_T("Visit our site"));

	default:
		ATLASSERT (!"Unexpected button ID");
		break;
	}
	return _T("");
}

CRect CMainWindow::_GetChildRect(_IDS id) {
	const int paddingX = 10, paddingY = 10;
	const int ctrlMargin = 8;
	const int buttonPadding = 8;
	const int staticPadding = 2;
	const int buttonWidth = 120;
	const int previewWidth = 120;
	const int previewHeight = 160;
	const int donationWidth = previewWidth;
	const int siteLinkWidth = previewWidth;
	int ctrlHeight = m_fontHeight + buttonPadding * 2;
	int staticHeight = m_fontHeight + staticPadding * 2;
	CRect rc;
	GetClientRect(rc);
	rc.DeflateRect(paddingX, paddingY, paddingX, paddingY);
	if (id == ID_TAB) {
		rc.bottom -= ctrlMargin + ctrlHeight;
	} else if (id == ID_ADD_IMAGE) {
		rc.top = rc.bottom - ctrlHeight;
		rc.left += buttonWidth + ctrlMargin;
		rc.right = rc.left + buttonWidth;
	} else if (id == ID_START_UPLOAD || id == ID_STOP_UPLOAD) {
		rc.top = rc.bottom - ctrlHeight;
		rc.right = rc.left + buttonWidth;
	} else if (id == ID_CLEAR_ALL) {
		rc.top = rc.bottom - ctrlHeight;
		rc.left += buttonWidth * 2 + ctrlMargin * 2;
		rc.right = rc.left + buttonWidth;
	} else if (id == ID_ABOUT) {
		rc.top = rc.bottom - ctrlHeight;
		rc.left = rc.right - buttonWidth;
	} else if (id == ID_SITE_LINK) {
		rc.bottom = rc.top + staticHeight;
		rc.left = rc.right - siteLinkWidth;
	} else {
		if (m_tab.m_hWnd) {
			m_tab.GetClientRect(rc);
			m_tab.AdjustRect(false, rc);
			m_tab.ClientToScreen(rc);
			ScreenToClient(rc);
			rc.DeflateRect(ctrlMargin, ctrlMargin, ctrlMargin, ctrlMargin);
		} else {
			rc.DeflateRect(ctrlMargin, ctrlMargin + m_tabHeight, ctrlMargin, ctrlMargin);
		}

		if (id == ID_SITE_LIST) {
			rc.top += ctrlMargin;
			rc.right = rc.left + 160;
			rc.bottom = rc.top + 200;
		} else if (id == ID_FILE_LIST) {
			rc.top += ctrlHeight + ctrlMargin;
			rc.right -= previewWidth + ctrlMargin;
		} else if (id == ID_PREVIEW) {
			rc.top += ctrlHeight + ctrlMargin;
			rc.bottom = rc.top + previewHeight;
			rc.left = rc.right - previewWidth;
		} else if (id == ID_MAKE_DONATION) {
			rc.left = rc.right - donationWidth;
			rc.top = rc.bottom - staticHeight;
		} else if (id >= ID_EDIT_BEGIN && id < ID_EDIT_END) {
			int index = id - ID_EDIT_BEGIN;
			rc.top += staticHeight + ctrlMargin;
			rc.bottom -= ctrlMargin + ctrlHeight;
			int count = ID_EDIT_END - ID_EDIT_BEGIN;
			int width = (rc.Width() - (count - 1) * ctrlMargin) / count;
			rc.left += index * (width + ctrlMargin);
			rc.right = rc.left + width;
		} else if (id >= ID_STATIC_BEGIN && id < ID_STATIC_END) {
			int index = id - ID_STATIC_BEGIN;
			int count = ID_STATIC_END - ID_STATIC_BEGIN;
			int width = (rc.Width() - (count - 1) * ctrlMargin) / count;
			rc.left += index * (width + ctrlMargin);
			rc.right = rc.left + width;
			rc.bottom = rc.top + staticHeight;
		} else if (id >= ID_BUTTON_COPY_BEGIN && id < ID_BUTTON_COPY_END) {
			int index = id - ID_BUTTON_COPY_BEGIN;
			int count = ID_BUTTON_COPY_END - ID_BUTTON_COPY_BEGIN;
			int width = (rc.Width() - (count - 1) * ctrlMargin) / count;
			rc.left += index * (width + ctrlMargin);
			rc.right = rc.left + width;
			rc.top = rc.bottom - ctrlHeight;
		} else {
			ATLASSERT (false);
		}
	}
	return rc;
}

bool CMainWindow::_CreateButtons() {
	HFONT hFont = (HFONT)::GetStockObject(DEFAULT_GUI_FONT);
	for (int id = (int)ID_BUTTON_BEGIN; id < (int)ID_BUTTON_END; ++ id) {
		tstring text = _GetCtrlText((_IDS)id);
		CRect rc = _GetChildRect((_IDS)id);
		DWORD dwStyle = WS_CHILD;
		if (id != ID_STOP_UPLOAD) {
			dwStyle |= WS_VISIBLE;
		}
		int index = id - (int)ID_BUTTON_BEGIN;
		HWND hWnd = m_buttons[index].Create(m_hWnd, rc, text.c_str(), dwStyle, 0, (_IDS)id);
		if (hWnd == NULL) {
			return false;
		}
		m_id2hwnd[(_IDS)id] = hWnd;
		::SendMessage(hWnd, WM_SETFONT, (WPARAM)hFont, TRUE);
	}
	return true;
}

bool CMainWindow::_CreateEdits() {
	HFONT hFont = (HFONT)::GetStockObject(DEFAULT_GUI_FONT);
	for (int id = (int)ID_EDIT_BEGIN; id < (int)ID_EDIT_END; ++ id) {
		tstring text;
		CRect rc = _GetChildRect((_IDS)id);
		DWORD dwStyle = WS_CHILD | WS_VSCROLL;
		DWORD dwEditStyle = ES_MULTILINE | ES_READONLY | ES_AUTOVSCROLL | ES_NOHIDESEL;
		DWORD dwExStyle = WS_EX_CLIENTEDGE;
		int index = id - (int)ID_EDIT_BEGIN;
		HWND hWnd = m_edits[index].Create(m_hWnd, rc, text.c_str(), dwStyle | dwEditStyle, dwExStyle, (_IDS)id);
		if (hWnd == NULL) {
			return false;
		}
		m_id2hwnd[(_IDS)id] = hWnd;
		::SendMessage(hWnd, WM_SETFONT, (WPARAM)hFont, TRUE);
	}
	return true;
}

bool CMainWindow::_CreateStatics() {
	HFONT hFont = (HFONT)::GetStockObject(DEFAULT_GUI_FONT);
	for (int id = (int)ID_STATIC_BEGIN; id < (int)ID_STATIC_END; ++ id) {
		CRect rc = _GetChildRect((_IDS)id);
		DWORD dwStyle = WS_CHILD;
		DWORD dwExStyle = 0;
		int index = id - (int)ID_STATIC_BEGIN;
		HWND hWnd = m_statics[index].Create(m_hWnd, rc, _T(""), dwStyle, dwExStyle, (_IDS)id);
		if (hWnd == NULL) {
			return false;
		}
		m_id2hwnd[(_IDS)id] = hWnd;
		::SendMessage(hWnd, WM_SETFONT, (WPARAM)hFont, TRUE);
		tstring text = _GetCtrlText((_IDS)id);
		m_statics[index].SetWindowText(text.c_str());
	}
	return true;
}

bool CMainWindow::_CreateHyperLinks() {
	HFONT hFont = (HFONT)::GetStockObject(DEFAULT_GUI_FONT);

	_IDS ids[] = {ID_SITE_LINK, ID_MAKE_DONATION};
	CHyperLink *ctrls[] = {&m_hyperSite, &m_hyperMakeDonation};
	TCHAR *links[] = {
		_T("http://www.xilou.us"),
		_T("https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=10694914")
	};

	// if P.R.C, use xilou.cn
	LCID lcid = GetSystemDefaultLCID();
	lcid &= 0xffff;
	if (lcid == 0x0804) {
		links[0] = _T("http://www.xilou.cn");
	}

	for (int i = 0; i < COUNT_OF(ids); ++ i) {
		int id = (int)ids[i];
		CHyperLink *ctrl = ctrls[i];
		TCHAR *link = links[i];
		CRect rc = _GetChildRect((_IDS)id);
		DWORD dwStyle = WS_CHILD;
		DWORD dwExStyle = 0;
		if (id == ID_SITE_LINK) {
			dwStyle |= WS_VISIBLE;
		}
		HWND hWnd = ctrl->Create(m_hWnd, rc, _T(""), dwStyle, dwExStyle, (_IDS)id);
		if (hWnd == NULL) {
			return false;
		}
		m_id2hwnd[(_IDS)id] = hWnd;
		::SendMessage(hWnd, WM_SETFONT, (WPARAM)hFont, TRUE);
		tstring text = _GetCtrlText((_IDS)id);
		ctrl->SetWindowText(text.c_str());
		ctrl->setLink(link);
	}

	HICON iconHome = ::LoadIcon(_Module.get_m_hInst(), MAKEINTRESOURCE(IDI_HOMEICON));
	m_hyperSite.setIcon(iconHome);

	return true;
}

bool CMainWindow::_CreateCopyButtons() {
	HFONT hFont = (HFONT)::GetStockObject(DEFAULT_GUI_FONT);
	for (int id = (int)ID_BUTTON_COPY_BEGIN; id < (int)ID_BUTTON_COPY_END; ++ id) {
		tstring text = _GetCtrlText((_IDS)id);
		CRect rc = _GetChildRect((_IDS)id);
		DWORD dwStyle = WS_CHILD;
		int index = id - (int)ID_BUTTON_COPY_BEGIN;
		HWND hWnd = m_copyButtons[index].Create(m_hWnd, rc, text.c_str(), dwStyle, 0, (_IDS)id);
		if (hWnd == NULL) {
			return false;
		}
		m_id2hwnd[(_IDS)id] = hWnd;
		::SendMessage(hWnd, WM_SETFONT, (WPARAM)hFont, TRUE);
	}
	return true;
}


bool CMainWindow::_CreateChildren() {
	HFONT hFont = (HFONT)::GetStockObject(DEFAULT_GUI_FONT);
	HWND hWnd;
	CRect rc;
	GetClientRect(rc);
	DWORD dwStyle = WS_CHILD | WS_VISIBLE;

	// tab
	hWnd = m_tab.Create(m_hWnd, _GetChildRect(ID_TAB),
	                    NULL,
	                    dwStyle | WS_CLIPCHILDREN | WS_CLIPSIBLINGS
			    | TCS_FIXEDWIDTH | WS_TABSTOP | TCS_FOCUSNEVER,
	                    WS_EX_CONTROLPARENT,
	                    ID_TAB
	                   );
	if (hWnd == NULL) {
		return false;
	}
	m_id2hwnd[ID_TAB] = hWnd;
	::SendMessage(hWnd, WM_SETFONT, (WPARAM)hFont, TRUE);

	CLanguage *pLanguage = CLanguage::getInstance();
	tstring tszTabs[] = {
	                    pLanguage->getString(_T("Images")),
			    pLanguage->getString(_T("Result")),
	                   };
	for (int i = 0; i < COUNT_OF(tszTabs); ++ i) {
		m_tab.InsertItem(i, tszTabs[i].c_str());
	}
	m_tab.SetItemSize(80, m_tabHeight);
	// m_tab.SetPadding()

	// site list
	hWnd = m_siteSelector.Create(m_hWnd,
	                             _GetChildRect(ID_SITE_LIST),
	                             NULL, 
	                             dwStyle | CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP, 
	                             0, 
	                             ID_SITE_LIST
	                            );
	if (hWnd == NULL) {
		return false;
	}
	m_id2hwnd[ID_SITE_LIST] = hWnd;
	::SendMessage(hWnd, WM_SETFONT, (WPARAM)hFont, TRUE);

	CSiteManager *pSiteManager = CSiteManager::getInstance();
	for (int i = 0; i < (int)pSiteManager->getSiteCount(); ++ i) {
		ISite *pSite = pSiteManager->getSiteByIndex(i);
		TCHAR *pszName = pSite->getSiteName();
		m_siteSelector.AddString(pszName);
	}
	m_siteSelector.SetCurSel(0);

	// file list
	hWnd = m_fileList.createWindow();
	if (hWnd == NULL) {
		return false;
	}
	m_id2hwnd[ID_FILE_LIST] = hWnd;

	// preview
	hWnd = m_imageCtrl.create(m_hWnd, 0, 0, dwStyle, 0, ID_PREVIEW);
	if (hWnd == NULL) {
		return false;
	}
	m_id2hwnd[ID_PREVIEW] = hWnd;

	// buttons
	if (!_CreateButtons()) {
		return false;
	}

	// edits
	if (!_CreateEdits()) {
		return false;
	}

	// statics
	if (!_CreateStatics()) {
		return false;
	}

	// hyper links
	if (!_CreateHyperLinks()) {
		return false;
	}

	// copy buttons
	if (!_CreateCopyButtons()) {
		return false;
	}

	return true;
}

void CMainWindow::_LayoutChildren() {
	// In _GetChildRect(), the control in (logically) the tab will use
	// m_tab.GetClientRect() to get its position, but in (2) we must use
	// DeferXXX to move the controls together, the client rect of the tab
	// will be wrong.
	// So I must resize the tab first.
	// 1. resize tab first
	CRect rc = _GetChildRect(ID_TAB);
	HWND hWnd = m_id2hwnd[ID_TAB];
	::SetWindowPos(hWnd, NULL, rc.left, rc.top, rc.Width(), rc.Height(), SWP_NOMOVE | SWP_NOZORDER);

	// 2. resize all
	HDWP hDwp = BeginDeferWindowPos((int)ID_CONTROL_END);
	for (int i = (int)ID_CONTROL_BEGIN; i < (int)ID_CONTROL_END; ++ i) {
		_IDS id = (_IDS)i;
		rc = _GetChildRect(id);
		hWnd = m_id2hwnd[id];
		ATLASSERT (hWnd && ::IsWindow(hWnd));

		if (id == ID_TAB) {
			::DeferWindowPos(hDwp, hWnd, NULL,
			                 rc.left, rc.top, rc.Width(), rc.Height(), 
			                 SWP_NOMOVE | SWP_NOZORDER);
		} else {
			if (::GetParent(hWnd) == m_hWnd) {
				::DeferWindowPos(hDwp,
						 hWnd, HWND_TOP, rc.left, rc.top, rc.Width(), rc.Height(),
						 0);
			}
		}
	}
	::EndDeferWindowPos(hDwp); 

	m_fileList.adjustColumnWidth();
}

void CMainWindow::_UpdateChildren() {
	CxDocument *pDoc = getDocument();
	CUploader *pUploader = CUploader::getInstance();

	int currTab = m_tab.GetCurSel();
	if (currTab == 0) {

		m_siteSelector.EnableWindow(pUploader->getWorkingThreadCount() == 0);

		for (int id = (int)ID_TABFILES_BEGIN; id < (int)ID_TABFILES_END; ++ id) {
			HWND hWnd = m_id2hwnd[(_IDS)id];
			::ShowWindow(hWnd, SW_SHOW);
		}
		for (int id = (int)ID_TABCODE_BEGIN; id < (int)ID_TABCODE_END; ++ id) {
			HWND hWnd = m_id2hwnd[(_IDS)id];
			::ShowWindow(hWnd, SW_HIDE);
		}
	} else if (currTab == 1) {
		for (int id = (int)ID_TABFILES_BEGIN; id < (int)ID_TABFILES_END; ++ id) {
			HWND hWnd = m_id2hwnd[(_IDS)id];
			::ShowWindow(hWnd, SW_HIDE);
		}
		for (int id = (int)ID_TABCODE_BEGIN; id < (int)ID_TABCODE_END; ++ id) {
			HWND hWnd = m_id2hwnd[(_IDS)id];
			::ShowWindow(hWnd, SW_SHOW);
		}
		size_t count = pDoc->getFileCountByStatus(FileInfo::US_OK);
		for (int id = (int)ID_BUTTON_COPY_BEGIN; id < (int)ID_BUTTON_COPY_END; ++ id) {
			int index = id - ID_BUTTON_COPY_BEGIN;
			m_copyButtons[index].EnableWindow(count > 0);
		}
	}

	// start / stop, add image..., and clear 3 buttons
	if (pUploader->getWorkingThreadCount() > 0) {
		m_buttons[ID_START_UPLOAD - ID_BUTTON_BEGIN].ShowWindow(SW_HIDE);
		m_buttons[ID_STOP_UPLOAD - ID_BUTTON_BEGIN].ShowWindow(SW_SHOW);

		m_buttons[ID_ADD_IMAGE - ID_BUTTON_BEGIN].EnableWindow(false);
		m_buttons[ID_START_UPLOAD - ID_BUTTON_BEGIN].EnableWindow(false);
		m_buttons[ID_CLEAR_ALL - ID_BUTTON_BEGIN].EnableWindow(false);
	} else {
		m_buttons[ID_START_UPLOAD - ID_BUTTON_BEGIN].ShowWindow(SW_SHOW);
		m_buttons[ID_STOP_UPLOAD - ID_BUTTON_BEGIN].ShowWindow(SW_HIDE);

		size_t count = pDoc->getFileCountByStatus(FileInfo::US_TODO) 
			+ pDoc->getFileCountByStatus(FileInfo::US_FAILED);
		m_buttons[ID_START_UPLOAD - ID_BUTTON_BEGIN].EnableWindow(count > 0);

		m_buttons[ID_CLEAR_ALL - ID_BUTTON_BEGIN].EnableWindow(pDoc->getFileCount() > 0);

		m_buttons[ID_ADD_IMAGE - ID_BUTTON_BEGIN].EnableWindow(true);
	}
}

void CMainWindow::_UpdateCode() {
	std::stringstream ss[CT_NUM] = {
		std::stringstream(std::ios_base::out),
		std::stringstream(std::ios_base::out),
		std::stringstream(std::ios_base::out),
	};
	size_t count = m_document.getFileCount();
	for (size_t i = 0; i < count; ++ i) {
		const FileInfo &fi = m_document.getFileByIndex(i);
		if (fi.status == FileInfo::US_OK) {
			_ExportCode(ss[CT_URL], fi.url, CT_URL);
			_ExportCode(ss[CT_UBB], fi.url, CT_UBB);
			_ExportCode(ss[CT_HTML], fi.url, CT_HTML);
		}
	}

	for (int i = 0; i < (int)CT_NUM; ++ i) {
		tstring text = util_mbs2tcs(ss[i].str());
		m_edits[i].SetWindowText(text.c_str());
	}
}

void CMainWindow::_OpenContentMenu(const CPoint &pt) {
	CMenu menu;
	CLanguage *pLanguage = CLanguage::getInstance();
	CUploader *pUploader = CUploader::getInstance();
	tstring str;
	if (menu.CreatePopupMenu()) {
		UINT flags = MF_STRING | TPM_TOPALIGN | TPM_LEFTALIGN;
		UINT additional = 0;
		if (m_fileList.GetSelectedCount() == 0 || pUploader->getWorkingThreadCount() > 0) {
			additional = MF_GRAYED;
		}
		str = pLanguage->getString(_T("Remove"));
		menu.AppendMenu(flags | additional, ID_CMD_REMOVE_SELECTED, str.c_str());
		additional = 0;
		if (m_fileList.GetItemCount() == 0 || pUploader->getWorkingThreadCount() > 0) {
			additional = MF_GRAYED;
		}
		str = pLanguage->getString(_T("Clear"));
		menu.AppendMenu(flags | additional, ID_CMD_CLEAR_ALL, str.c_str());
		additional = 0;

		menu.AppendMenu(MF_SEPARATOR, (unsigned)0, _T(""));

		bool allOK = m_fileList.GetSelectedCount() == 0 ? false : true;
		if (allOK) {
			int index = -1;
			while ((index = m_fileList.GetNextItem(index, LVNI_SELECTED)) != -1) {
				FileInfo::ID id = m_fileList.GetItemData(index);
				ATLASSERT (id != FileInfo::INVALID_ID);
				CxDocument::INDEX idx = m_document.getIndexById(id);
				ATLASSERT (idx != CxDocument::INVALID_INDEX);
				const FileInfo &fi = m_document.getFileByIndex(idx);
				if (fi.status != FileInfo::US_OK) {
					allOK = false;
					break;
				}
			}
		}
		if (!allOK) {
			additional = MF_GRAYED;
		}
		str = pLanguage->getString(_T("Copy URL"));
		menu.AppendMenu(flags | additional, ID_CMD_COPY_SELECTED_URL, str.c_str());
		str = pLanguage->getString(_T("Copy UBB"));
		menu.AppendMenu(flags | additional, ID_CMD_COPY_SELECTED_UBB, str.c_str());
		str = pLanguage->getString(_T("Copy HTML"));
		menu.AppendMenu(flags | additional, ID_CMD_COPY_SELECTED_HTML, str.c_str());

		menu.TrackPopupMenu(TPM_LEFTALIGN, pt.x, pt.y, m_hWnd);
		menu.DestroyMenu();
	}
}

void CMainWindow::_CopyToClipBoard(const tstring &text, bool asHtml /* = false */) {
	CLanguage *pLanguage = CLanguage::getInstance();
	tstring str;
	if (OpenClipboard()) {
		if (EmptyClipboard()) {
			HANDLE hData = NULL;
			const TCHAR *szText = text.c_str();
			int len = text.length() + 1;
			HGLOBAL hBuffer  = GlobalAlloc(GMEM_MOVEABLE, len * sizeof(TCHAR));
			TCHAR *buf = (TCHAR *)GlobalLock(hBuffer);
			_tcscpy_s(buf, len, szText);
			GlobalUnlock(hBuffer);
			buf = NULL;

#ifdef UNICODE
			hData = SetClipboardData(CF_UNICODETEXT, hBuffer);
#else
			hData = SetClipboardData(CF_TEXT, hBuffer);
#endif
			if (hData != NULL && asHtml) {
				std::string utf8;
#ifdef UNICODE
				utf8 = util_w2utf8(text);
#else
				utf8 = text;
#endif
				std::string html = util_clipboard_get_html_format(utf8);
				HGLOBAL hBuffer  = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, html.length());
				char *buf = (char *)GlobalLock(hBuffer);
				strcpy_s(buf, html.length(), html.c_str());
				GlobalUnlock(hBuffer);
				buf = NULL;

				UINT format = RegisterClipboardFormat(_T("HTML Format"));
				hData = SetClipboardData(format, hBuffer);
			}

			if (hData == NULL) {
				str = pLanguage->getString(_T("Copy data failed"));
				MessageBox(str.c_str(), _T(""), MB_OK | MB_ICONERROR);
			} else {
// 				str = pLanguage->getString(_T("Copy data success"));
// 				MessageBox(str.c_str(), _T("OK"), MB_OK | MB_ICONINFORMATION);
			}
		}
		CloseClipboard();
	} else {
		str = pLanguage->getString(_T("Open Clipboard failed"));
		MessageBox(str.c_str(), _T(""), MB_OK | MB_ICONERROR);
	}
}

void CMainWindow::_ExportCode(std::stringstream &ss, const std::string &url, CMainWindow::_CODE_TYPE ct) {
	if (ct == CT_URL) {
		ss << url << "\r\n\r\n";
	} else if (ct == CT_UBB) {
		ss << "[img]" << url << "[/img]\r\n\r\n";
	} else if (ct == CT_HTML) {
		ss << "<img alt=\"\" src=\"" << url << "\" />\r\n<br />\r\n<br />\r\n";
	} else {
		ATLASSERT (false);
	}
}

void CMainWindow::_UpdateTitle() {
	TCHAR title[MAX_PATH];
	CLanguage *pLanguage = CLanguage::getInstance();

	if (m_document.getFileCountByStatus(FileInfo::US_UPLOADING) > 0) {
		tstring format = pLanguage->getString(_T("TitleFormatUploading"));
		ATLASSERT (format != _T("TitleFormatUploading"));
		_stprintf_s(title, MAX_PATH, format.c_str(),
			m_lpszWindowName,
			m_document.getFileCount(),
			m_document.getFileCountByStatus(FileInfo::US_OK),
			m_document.getFileCountByStatus(FileInfo::US_UPLOADING),
			m_document.getFileCountByStatus(FileInfo::US_FAILED)
			);
		SetWindowText(title);
	} else if (m_document.getFileCount() > 0) {
		if (m_document.getFileCount() == m_document.getFileCountByStatus(FileInfo::US_OK)) {
			tstring format = pLanguage->getString(_T("TitleFormatOK"));
			ATLASSERT (format != _T("TitleFormatOK"));
			_stprintf_s(title, MAX_PATH, format.c_str(),
				m_lpszWindowName,
				m_document.getFileCount()
				);
		} else if (m_document.getFileCount() == m_document.getFileCountByStatus(FileInfo::US_TODO)) {
			tstring format = pLanguage->getString(_T("TitleFormatTodo"));
			ATLASSERT (format != _T("TitleFormatTodo"));
			_stprintf_s(title, MAX_PATH, format.c_str(),
				m_lpszWindowName,
				m_document.getFileCount()
				);
		} else {
			tstring format = pLanguage->getString(_T("TitleFormatStopped"));
			ATLASSERT (format != _T("TitleFormatStopped"));
			_stprintf_s(title, MAX_PATH, format.c_str(),
				m_lpszWindowName,
				m_document.getFileCount(),
				m_document.getFileCountByStatus(FileInfo::US_OK),
				m_document.getFileCountByStatus(FileInfo::US_FAILED)
				);
		}
		SetWindowText(title);
	} else {
		SetWindowText(m_lpszWindowName);
	}
}


const CMainWindow::FileExtSupported&
CMainWindow::_GetFileExtSupported() {
	static FileExtSupported exts;
	if (exts.size() == 0) {
		exts.push_back(_T("jpg"));
		exts.push_back(_T("jpeg"));
		exts.push_back(_T("gif"));
		exts.push_back(_T("png"));
	}

	return exts;
}

tstring CMainWindow::_GetOpenFileFilter() {
	CLanguage *pLanguage = CLanguage::getInstance();
	tstring filter = pLanguage->getString(_T("All Support Format")), temp;
	FileExtSupported::const_iterator it;
	const FileExtSupported& exts = _GetFileExtSupported();
	bool jpg = false, gif = false, png = false;
	for (it = exts.begin(); it != exts.end(); ++ it) {
		temp += _T("*.");
		temp += *it;
		temp += _T(";");
		const TCHAR *ext = it->c_str();
		if (_tcsicmp(ext, _T("jpg")) == 0 || _tcsicmp(ext, _T("jpeg")) == 0) {
			jpg = true;
		} else if (_tcsicmp(ext, _T("gif")) == 0) {
			gif = true;
		} else if (_tcsicmp(ext, _T("png")) == 0) {
			png = true;
		}
	}
	filter += _T(" (") + temp + _T(")");
	filter.append(_T("\0"), 1);
	filter += temp;

	if (jpg) {
		filter.append(_T("\0"), 1);
		filter += _T("JPEG (*.jpg;*.jpeg)");
		filter.append(_T("\0"), 1);
		filter += _T("*.jpg;*jpeg;");
	}
	if (gif) {
		filter.append(_T("\0"), 1);
		filter += _T("GIF (*.gif)");
		filter.append(_T("\0"), 1);
		filter += _T("*.gif;");
	}
	if (png) {
		filter.append(_T("\0"), 1);
		filter += _T("PNG (*.png)");
		filter.append(_T("\0"), 1);
		filter += _T("*.png;");
	}

	filter.append(_T("\0\0\0"), 3);

	return filter;
}

void CMainWindow::_DisplayAddFileError(const tstring &failed, const tstring &exists) {
	if (exists.length() > 0 || failed.length() > 0) {
		CLanguage *pLanguage = CLanguage::getInstance();
		tstring e = pLanguage->getString(_T("BelowExists:"));
		tstring f = pLanguage->getString(_T("AddBelowFailed:"));
		tstring info;
		if (failed.length() > 0) {
			info += f;
			info += _T("\r\n");
			info += failed;
			info += _T("\r\n");
		}
		if (exists.length() > 0) {
			info += e;
			info += _T("\r\n");
			info += exists;
		}
		MessageBox(info.c_str(), NULL, 
			failed.length() == 0 ? MB_ICONINFORMATION : MB_ICONWARNING);
	}
}

bool CMainWindow::_AddFile(const TCHAR *pathname, bool &exists) {
	ATLASSERT (pathname);
	exists = false;

	// check extension
	const TCHAR *ext = _tcsrchr(pathname, _T('.'));
	if (!ext) {
		return false;
	}
	++ ext; // skip the '.'
	const FileExtSupported &exts = _GetFileExtSupported();
	bool matched = false;
	FileExtSupported::const_iterator it;
	for (it = exts.begin(); it != exts.end(); ++ it) {
		if (_tcsicmp(it->c_str(), ext) == 0) {
			matched = true;
			break;
		}
	}
	if (!matched) {
		return false;
	}

	const int bufLen = MAX_PATH * 2;
	FileInfo::ID id = m_document.addFile(pathname);
	if (id == FileInfo::INVALID_ID
	    && m_document.getLastError() == ERROR_ALREADY_EXISTS) {
		exists = true;
		return true;
	}

	if (id == FileInfo::INVALID_ID) {
		return false;
	}

	return true;
}

bool CMainWindow::_StartUpload() {
	// reset failed to todo
	size_t count = m_document.getFileCount();
	for (size_t i = 0; i < count; ++ i) {
		const FileInfo &fi = m_document.getFileByIndex(i);
		if (fi.status == FileInfo::US_FAILED) {
			m_document.setFileUploadResult(fi.id, FileInfo::US_TODO, "");
		}
	}

	m_buttons[ID_START_UPLOAD - ID_BUTTON_BEGIN].ShowWindow(SW_HIDE);
	m_buttons[ID_STOP_UPLOAD - ID_BUTTON_BEGIN].ShowWindow(SW_SHOW);

	CUploader *pUploader = CUploader::getInstance();
	return pUploader->startUpload();
}

bool CMainWindow::_StopUpload() {
	m_buttons[ID_START_UPLOAD - ID_BUTTON_BEGIN].ShowWindow(SW_SHOW);
	m_buttons[ID_STOP_UPLOAD - ID_BUTTON_BEGIN].ShowWindow(SW_HIDE);
	CUploader *pUploader = CUploader::getInstance();
	return pUploader->stopUpload();
}



//////////////////////////////////////////////////////////////////////////
// public methods

CMainWindow* CMainWindow::getInstance() {
	static CMainWindow mainWindow;
	return &mainWindow;
}

BOOL CMainWindow::PreTranslateMessage(MSG* pMsg) {
	return FALSE;
}

BOOL CMainWindow::OnIdle() {
	_UpdateChildren();
	return FALSE;
}

CRect CMainWindow::getFileListViewCtrlRect() {
	return _GetChildRect(ID_FILE_LIST);
}

UINT CMainWindow::getFileListViewCtrlId() {
	return ID_FILE_LIST;
}

HWND CMainWindow::getFileListViewCtrlParentHwnd() {
	return m_hWnd;
}


CxDocument* CMainWindow::getDocument() {
	return &m_document;
}

void CMainWindow::onEvent(EVT evt, void *param) {
	if (m_titleUpdated) {
		m_titleUpdated = false;
		PostMessage(WM_APP_UPDATE_MAIN_TITLE, 0, 0);
	}
}


HWND CMainWindow::createWindow() {
	ATOM atom = GetWndClassInfo().Register(&m_pfnSuperWindowProc);

	DWORD dwStyle = GetWndStyle(0);
	DWORD dwExStyle = GetWndExStyle(0);

	// window position
	int width = MIN_WIDTH;
	int height = MIN_HEIGHT;
	int screenWidth = ::GetSystemMetrics(SM_CXSCREEN);
	int screenHeight = ::GetSystemMetrics(SM_CYSCREEN);
	CRect rcPos(CPoint((screenWidth - width) / 2, (screenHeight - height) / 2), CSize(width, height));

	_U_RECT rect(rcPos);	
	return _Base::Create(NULL, rect.m_lpRect, m_lpszWindowName, dwStyle, dwExStyle, (HMENU)NULL, atom, NULL);
}

ISite* CMainWindow::getSelectedSite() {
	int siteIndex = m_siteSelector.GetCurSel();
	CSiteManager *pSiteManager = CSiteManager::getInstance();
	ISite *pSite = pSiteManager->getSiteByIndex(siteIndex);
	return pSite;
}


//////////////////////////////////////////////////////////////////////////
// msg handler

LRESULT CMainWindow::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) {
	HICON hIcon = ::LoadIcon(_Module.get_m_hInst(), MAKEINTRESOURCE(IDI_MAINICON));
	SetClassLong(m_hWnd, GCL_HICON, (LONG)hIcon);
	SetClassLong(m_hWnd, GCL_HICONSM, (LONG)hIcon);

	HFONT hFont = (HFONT)::GetStockObject(DEFAULT_GUI_FONT);
	LOGFONT lf;
	::GetObject(hFont, sizeof(lf), &lf);
	m_fontHeight = lf.lfHeight > 0 ? lf.lfHeight : -lf.lfHeight;

	_CreateChildren();
	m_document.subscribe(this);

	DragAcceptFiles(TRUE);

	// layered window
#ifdef UI_EFFECT_BACKGROUND_TRANSPARENT
	DWORD exStyle = GetExStyle();
	if (!(exStyle & WS_EX_LAYERED)) {
		exStyle |= WS_EX_LAYERED;
		SetWindowLong(GWL_EXSTYLE, exStyle);
	}
	::SetLayeredWindowAttributes(m_hWnd, 0, 255, LWA_ALPHA);
#endif

	return TRUE;
}

LRESULT CMainWindow::OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) {
	DragAcceptFiles(FALSE);
	m_document.unsubscribe(this);
	PostMessage(WM_QUIT);
	return TRUE;
}

LRESULT CMainWindow::OnDrawItem(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) {
	DRAWITEMSTRUCT *pdis = (DRAWITEMSTRUCT *)lParam;
	if (pdis->CtlID != ID_FILE_LIST) {
		bHandled = false;
		return 0;
	}

	m_fileList.drawItem(pdis);
	return true;
}

LRESULT CMainWindow::OnMeasureItem(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) {
	MEASUREITEMSTRUCT *pmis = (MEASUREITEMSTRUCT *)lParam;
	if (pmis->CtlID != ID_FILE_LIST) {
		bHandled = false;
		return 0;
	}

	m_fileList.measureItem(wParam, lParam);
	return true;
}


LRESULT CMainWindow::OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) {
	CRect rc;
	GetClientRect(rc);

	PAINTSTRUCT ps;
	HDC hdc = BeginPaint(&ps);
	CDCHandle dc(hdc);
	// dc.FillSolidRect(rc, ::GetSysColor(COLOR_3DFACE));

	EndPaint(&ps);
	return TRUE;
}

LRESULT CMainWindow::OnEraseBkGnd(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) {
	bHandled = false;
	return 1;
}

LRESULT CMainWindow::OnCtlColorStatic(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) {
	HWND hWnd = (HWND)lParam;

	for (int i = 0; i < COUNT_OF(m_edits); ++ i) {
		if (m_edits[i].m_hWnd == hWnd) {
			return (LRESULT)::GetStockObject(WHITE_BRUSH);
		}
	}

	bHandled = false;
	return 0;
}

LRESULT CMainWindow::OnSize(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) {
	_LayoutChildren();
	return 0;
}

LRESULT CMainWindow::OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) {
	NMHDR *pnmhdr = (NMHDR *)lParam;
	if (pnmhdr->idFrom == ID_FILE_LIST) {
		UINT code = pnmhdr->code;
		if (code == LVN_ITEMCHANGED) { // select or de-select item(s)
			NMLISTVIEW *nmlv = (NMLISTVIEW *)lParam;
			FileInfo::ID id = m_fileList.getIdFromItem(nmlv->iItem);
			CxDocument::INDEX index = m_document.getIndexById(id);
			if (index != CxDocument::INVALID_INDEX && (nmlv->uNewState & LVIS_SELECTED)) {
				const FileInfo &fi = m_document.getFileByIndex(index);
				m_imageCtrl.setFile(fi.pathname);
			} else {
				m_imageCtrl.setFile(_T(""));
			}
		} else if (code == LVN_GETINFOTIP) {
			static TCHAR info[INFOTIPSIZE];
			NMLVGETINFOTIP *nmlv = (NMLVGETINFOTIP *)lParam;
			FileInfo::ID id = m_fileList.getIdFromItem(nmlv->iItem);
			CxDocument::INDEX index = m_document.getIndexById(id);
			if (index != CxDocument::INVALID_INDEX) {
				const FileInfo &fi = m_document.getFileByIndex(index);
				tstring s = fi.info;
				if (s.length() >= INFOTIPSIZE) {
					s = s.substr(0, INFOTIPSIZE - 1);
				}
				_tcscpy_s(info, INFOTIPSIZE, s.c_str());
				if (nmlv->pszText && nmlv->cchTextMax > 0) {
					size_t count = nmlv->cchTextMax;
					if (nmlv->cchTextMax > INFOTIPSIZE) {
						count = INFOTIPSIZE;
					}
					_tcscpy_s(nmlv->pszText, count, info);
				} else {
					nmlv->pszText = info;
				}
			}
		} else if (code == NM_RCLICK) { // right click 
			NMITEMACTIVATE *nmia = (NMITEMACTIVATE *)lParam;
			CPoint pt = nmia->ptAction;
			m_fileList.ClientToScreen(&pt);
			_OpenContentMenu(pt);
		}
	}
	return 0;
}


LRESULT CMainWindow::OnGetMinMaxInfo(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) {
	MINMAXINFO *pmmi = (MINMAXINFO *)lParam;
	pmmi->ptMinTrackSize.x = MIN_WIDTH;
	pmmi->ptMinTrackSize.y = MIN_HEIGHT;
	return 0;
}

LRESULT CMainWindow::OnDropFiles(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) {
	tstringstream ssExists, ssFailed;

	HDROP hDrop = (HDROP)wParam;
	TCHAR pathname[MAX_PATH];
	UINT fileCount = DragQueryFile(hDrop, (UINT)-1, NULL, 0);
	for (UINT i = 0; i < fileCount; ++ i) {
		if (DragQueryFile(hDrop, i, pathname, MAX_PATH) == 0) {
			break;
		}
		bool exists = false;
		if (_AddFile(pathname, exists) == false) {
			ssFailed << pathname << _T("\r\n");
		}
		if (exists) {
			ssExists << pathname << _T("\r\n");
		}
	}
	tstring exists = ssExists.str();
	tstring failed = ssFailed.str();
	_DisplayAddFileError(failed, exists);
	return 0;
}


LRESULT CMainWindow::OnCmdAddImage(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled) {

	tstring filter = _GetOpenFileFilter();
	CMultiFileDialog mfd(
	                     NULL,
	                     NULL, 
	                     OFN_HIDEREADONLY | OFN_FILEMUSTEXIST,
	                     filter.c_str(),
	                     m_hWnd
	                    );

	CSetting *pSetting = CSetting::getInstance();
	std::string strCurrDir = pSetting->getValue("currDir");
	tstring tstrCurrDir = util_mbs2tcs(strCurrDir);// it must have the same life with mfd.
	if (strCurrDir.length() > 0) {
		mfd.m_ofn.lpstrInitialDir = tstrCurrDir.c_str();
	}

#ifdef UI_EFFECT_BACKGROUND_TRANSPARENT
	::SetLayeredWindowAttributes(m_hWnd, 0, UI_EFFECT_BACKGROUND_TRANSPARENT, LWA_ALPHA);
#endif

	if (mfd.DoModal(m_hWnd) == IDOK) {
		// save currDir
		char currDir[MAX_PATH];
		GetCurrentDirectoryA(MAX_PATH, currDir);
		if (_stricmp(currDir, strCurrDir.c_str()) != 0) {
			pSetting->setValue("currDir", currDir);
			pSetting->save();
		}

		tstringstream ssExists, ssFailed;
		TCHAR pathname[MAX_PATH];
		int ret = mfd.GetFirstPathName(pathname, MAX_PATH);
		while (ret) {
			bool exists = false;
			if (_AddFile(pathname, exists) == false) {
				ssFailed << pathname << _T("\r\n");
			}
			if (exists) {
				ssExists << pathname << _T("\r\n");
			}
			ret = mfd.GetNextPathName(pathname, MAX_PATH);
		}
		tstring exists = ssExists.str();
		tstring failed = ssFailed.str();
		_DisplayAddFileError(failed, exists);
	}
#ifdef UI_EFFECT_BACKGROUND_TRANSPARENT
	::SetLayeredWindowAttributes(m_hWnd, 0, 255, LWA_ALPHA);
#endif

	return TRUE;
}

LRESULT CMainWindow::OnCmdStartUpload(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled) {
	return _StartUpload();
}

LRESULT CMainWindow::OnCmdStopUpload(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled) {
	return _StopUpload();
}

LRESULT CMainWindow::OnCmdClearAll(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled) {
	ATLASSERT (CUploader::getInstance()->getWorkingThreadCount() == 0);
	m_document.clear();
	_UpdateCode();
	return 0;
}

LRESULT CMainWindow::OnCmdAbout(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled) {
	CDlgAbout dlg;
#ifdef UI_EFFECT_BACKGROUND_TRANSPARENT
	::SetLayeredWindowAttributes(m_hWnd, 0, UI_EFFECT_BACKGROUND_TRANSPARENT, LWA_ALPHA);
#endif

	dlg.DoModal(m_hWnd);

#ifdef UI_EFFECT_BACKGROUND_TRANSPARENT
	::SetLayeredWindowAttributes(m_hWnd, 0, 255, LWA_ALPHA);
#endif
	return 0;
}

LRESULT CMainWindow::OnCmdRemoveSelected(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled) {
	int index = -1;
	std::vector<FileInfo::ID> ids;
	while ((index = m_fileList.GetNextItem(index, LVNI_SELECTED)) != -1) {
		FileInfo::ID id = m_fileList.GetItemData(index);
		ATLASSERT (id != FileInfo::INVALID_ID);
		ids.push_back(id);
	}

	for (std::vector<FileInfo::ID>::iterator it = ids.begin(); it != ids.end(); ++ it) {
		FileInfo::ID id = *it;
		ATLASSERT (m_document.getIndexById(id) != CxDocument::INVALID_INDEX);
		m_document.remove(id);
 	}

	_UpdateCode();
	return 0;
}

LRESULT CMainWindow::OnCmdCopy(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled) {
	int index = wID - ID_BUTTON_COPY_BEGIN;
	CEdit &edit = m_edits[index];
	int len = edit.GetWindowTextLength();
	tstring strText;
	if (len > 0) {
		TCHAR *buf = new TCHAR[len + 1];
		edit.GetWindowText(buf, len + 1);
		strText = buf;
		delete []buf;
	} else {
		return 0;
	}

	_CopyToClipBoard(strText, wID == ID_BUTTON_COPY_HTML);

	return 0;
}

LRESULT CMainWindow::OnCmdCopySelected(WORD wNotifyCode, WORD wID, HWND hWndCtl, BOOL& bHandled) {
	ATLASSERT (m_fileList.GetSelectedCount() > 0);
	_CODE_TYPE type = (_CODE_TYPE)(wID - (int)ID_CMD_COPY_SELECTED_BEGIN);
	ATLASSERT (type < CT_NUM);

	int index = -1;
	std::stringstream ss;
	while ((index = m_fileList.GetNextItem(index, LVNI_SELECTED)) != -1) {
		FileInfo::ID id = m_fileList.GetItemData(index);
		ATLASSERT (id != FileInfo::INVALID_ID);
		CxDocument::INDEX idx = m_document.getIndexById(id);
		ATLASSERT (idx != CxDocument::INVALID_INDEX);
		const FileInfo &fi = m_document.getFileByIndex(idx);
		ATLASSERT (fi.status == FileInfo::US_OK);
		_ExportCode(ss, fi.url, type);
	}
	tstring text = util_mbs2tcs(ss.str());
	_CopyToClipBoard(text, type == CT_HTML);
	return 0;
}

LRESULT CMainWindow::OnUploadFileSuccess(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) {
	_UpdateCode();
	return 0;
}

LRESULT CMainWindow::OnUploadThreadFinished(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) {

	_UpdateCode();

	CUploader *uploader = CUploader::getInstance();
	// if there are thread still working, just ignore this message
	if (uploader->getWorkingThreadCount() > 0) {
		return 0;
	}

	// user canceled
	uploader->resetUserCancel();

	return 0;
}

LRESULT CMainWindow::OnUpdateListView(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) {
	FileInfo::ID id = (FileInfo::ID)wParam;
	ATLASSERT(id != FileInfo::INVALID_ID);
	m_fileList.updateFile(id);
	return 0;
}


LRESULT CMainWindow::OnUpdateMainTitle(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled) {
	m_titleUpdated = true;
	_UpdateTitle();

	return 0;
}
